#------------------------------------------------------------------------------
# Include source and build directories
#------------------------------------------------------------------------------
set(MERCURY_BUILD_INCLUDE_DEPENDENCIES
  ${CMAKE_CURRENT_SOURCE_DIR}
  ${CMAKE_CURRENT_BINARY_DIR}
  ${CMAKE_CURRENT_SOURCE_DIR}/proc_extra
)

#------------------------------------------------------------------------------
# Internal dependencies (exported libs)
#------------------------------------------------------------------------------
# UTIL
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/util)
set(MERCURY_EXT_PKG_INCLUDE_DEPENDENCIES
  ${MERCURY_EXT_PKG_INCLUDE_DEPENDENCIES}
  ${MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES}
  )
set(MERCURY_EXT_PKG_LIB_DEPENDENCIES
  ${MERCURY_EXT_PKG_LIB_DEPENDENCIES}
  ${MERCURY_UTIL_EXT_LIB_DEPENDENCIES}
)
set(MERCURY_EXPORTED_LIBS ${MERCURY_EXPORTED_LIBS} mercury_util)

# NA
set(NA_INSTALL_BIN_DIR ${MERCURY_INSTALL_BIN_DIR})
set(NA_INSTALL_LIB_DIR ${MERCURY_INSTALL_LIB_DIR})
set(NA_INSTALL_INCLUDE_DIR ${MERCURY_INSTALL_INCLUDE_DIR})
set(NA_INSTALL_DATA_DIR ${MERCURY_INSTALL_DATA_DIR})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/na)
set(MERCURY_EXT_PKG_INCLUDE_DEPENDENCIES
  ${MERCURY_EXT_PKG_INCLUDE_DEPENDENCIES}
  ${NA_EXT_INCLUDE_DEPENDENCIES}
  )
set(MERCURY_EXT_PKG_LIB_DEPENDENCIES
  ${MERCURY_EXT_PKG_LIB_DEPENDENCIES}
  ${NA_EXT_LIB_DEPENDENCIES}
)
set(MERCURY_EXPORTED_LIBS ${MERCURY_EXPORTED_LIBS} na)

#------------------------------------------------------------------------------
# External dependencies
#------------------------------------------------------------------------------
# Boost (for preprocessor macros)
option(MERCURY_USE_BOOST_PP "Use BOOST preprocessor macros." OFF)
if(MERCURY_USE_BOOST_PP)
  option(MERCURY_USE_SYSTEM_BOOST "Use system-installed Boost libraries." OFF)
  if(MERCURY_USE_SYSTEM_BOOST)
    find_package(Boost 1.41.0 REQUIRED)
    message(STATUS "Boost include directory: ${Boost_INCLUDE_DIRS}")
    set(HG_HAS_BOOST 1)
    set(MERCURY_EXT_INCLUDE_DEPENDENCIES
      ${MERCURY_EXT_INCLUDE_DEPENDENCIES}
      ${Boost_INCLUDE_DIRS}
    )
  else()
    set(HG_HAS_BOOST 1)
    set(MERCURY_BUILD_INCLUDE_DEPENDENCIES
      ${MERCURY_BUILD_INCLUDE_DEPENDENCIES}
      ${CMAKE_CURRENT_SOURCE_DIR}/boost
    )
  endif()
endif()

# MChecksum
option(MERCURY_USE_CHECKSUMS "Checksum transfers of RPC arguments." ON)
if(MERCURY_USE_CHECKSUMS)
  set(HG_HAS_CHECKSUMS 1)
  option(MERCURY_USE_SYSTEM_MCHECKSUM "Use system-installed MChecksum." OFF)
  if(MERCURY_USE_SYSTEM_MCHECKSUM)
    find_package(mchecksum REQUIRED)
    set(MERCURY_EXT_PKG_DEPENDENCIES
      ${MERCURY_EXT_PKG_DEPENDENCIES}
      mchecksum
    )
  else()
    set(MCHECKSUM_EXTERNALLY_CONFIGURED 1)
    set(MCHECKSUM_INSTALL_BIN_DIR ${MERCURY_INSTALL_BIN_DIR})
    set(MCHECKSUM_INSTALL_LIB_DIR ${MERCURY_INSTALL_LIB_DIR})
    set(MCHECKSUM_INSTALL_INCLUDE_DIR ${MERCURY_INSTALL_INCLUDE_DIR})
    set(MCHECKSUM_INSTALL_DATA_DIR ${MERCURY_INSTALL_DATA_DIR})
    # TODO might need to fix MCHECKSUM_INSTALL_INCLUDE_INTERFACE from here
    set(MCHECKSUM_EXTERNAL_EXPORTED_TARGETS ${MERCURY_EXPORTED_TARGETS})
    add_subdirectory(mchecksum)
    include(${MCHECKSUM_BINARY_DIR}/mchecksum-config.cmake)
    set(MERCURY_EXPORTED_LIBS mchecksum ${MERCURY_EXPORTED_LIBS})
    get_target_property(MCHECKSUM_EXT_LIB_DEPENDENCIES mchecksum INTERFACE_LINK_LIBRARIES)
    if(MCHECKSUM_EXT_LIB_DEPENDENCIES)
      set(MERCURY_EXT_PKG_LIB_DEPENDENCIES
        ${MERCURY_EXT_PKG_LIB_DEPENDENCIES}
        ${MCHECKSUM_EXT_LIB_DEPENDENCIES}
      )
    endif()
  endif()
endif()

# Coresident RPC
option(MERCURY_USE_SELF_FORWARD "Execute RPC call locally and bypass NA layer when using NA self address." OFF)
if(MERCURY_USE_SELF_FORWARD)
  set(HG_HAS_SELF_FORWARD 1)
endif()
set(MERCURY_USE_SELF_FORWARD PARENT_SCOPE)

# Eager bulk transfer
option(MERCURY_USE_EAGER_BULK "Encode bulk data with RPC when bulk region described is small." ON)
if(MERCURY_USE_EAGER_BULK)
  set(HG_HAS_EAGER_BULK 1)
endif()

# Post limit
option(MERCURY_ENABLE_POST_LIMIT "Limit number of handles posted by listeners." ON)
if(MERCURY_ENABLE_POST_LIMIT)
  set(HG_HAS_POST_LIMIT 1)
endif()
mark_as_advanced(MERCURY_ENABLE_POST_LIMIT)
set(MERCURY_POST_LIMIT "256" CACHE STRING "Number of handles posted.")
mark_as_advanced(MERCURY_POST_LIMIT)

# Transparent shared-memory routing
option(MERCURY_USE_SM_ROUTING
  "Enable routing of local communication through shared-memory (requires SM)."
  OFF)
if(MERCURY_USE_SM_ROUTING)
  if(NOT NA_USE_SM)
    # Always force SM if NA_USE_SM is turned OFF
    set(NA_USE_SM "ON" CACHE BOOL "Use shared-memory plugin." FORCE)
  endif()
  set(HG_HAS_SM_ROUTING 1)
endif()

# Collect statistics
option(MERCURY_ENABLE_STATS "Enable collection of stats that can be printed at exit." OFF)
if(MERCURY_ENABLE_STATS)
  set(HG_HAS_COLLECT_STATS 1)
endif()

# XDR
option(MERCURY_USE_XDR "Use XDR for generic encoding." OFF)
if(MERCURY_USE_XDR)
  set(HG_HAS_XDR 1)
endif()

# For htonl etc
if(WIN32)
  set(MERCURY_EXT_LIB_DEPENDENCIES ${MERCURY_EXT_LIB_DEPENDENCIES} ws2_32)
endif(WIN32)

#------------------------------------------------------------------------------
# Configure module header files
#------------------------------------------------------------------------------
# Set unique vars used in the autogenerated config file (symbol import/export)
configure_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_config.h.in
  ${CMAKE_CURRENT_BINARY_DIR}/mercury_config.h
)

#------------------------------------------------------------------------------
# Set sources
#------------------------------------------------------------------------------
set(MERCURY_SRCS
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_bulk.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_core.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_core_header.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_header.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_proc.c
  ${CMAKE_CURRENT_SOURCE_DIR}/proc_extra/mercury_string_object.c
)
set(MERCURY_HL_SRCS
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hl.c
)

#------------------------------------------------------------------------------
# Libraries
#------------------------------------------------------------------------------

# Clean up system include path first
foreach(item ${MERCURY_SYSTEM_INCLUDE_PATH})
  if(MERCURY_EXT_INCLUDE_DEPENDENCIES)
    list(REMOVE_ITEM MERCURY_EXT_INCLUDE_DEPENDENCIES ${item})
  endif()
endforeach()

# MERCURY
add_library(mercury ${MERCURY_SRCS})
target_include_directories(mercury
  PUBLIC  "$<BUILD_INTERFACE:${MERCURY_BUILD_INCLUDE_DEPENDENCIES}>"
          $<INSTALL_INTERFACE:${MERCURY_INSTALL_INCLUDE_INTERFACE}>
)
target_include_directories(mercury
  SYSTEM PUBLIC ${MERCURY_EXT_INCLUDE_DEPENDENCIES}
)
target_link_libraries(mercury
  ${MERCURY_EXPORTED_LIBS}
  ${MERCURY_EXT_LIB_DEPENDENCIES}
  ${MERCURY_EXT_PKG_DEPENDENCIES}
  )
mercury_set_lib_options(mercury "mercury" ${MERCURY_LIBTYPE})
if(MERCURY_ENABLE_COVERAGE)
  set_coverage_flags(mercury)
endif()

set(MERCURY_EXPORTED_LIBS mercury ${MERCURY_EXPORTED_LIBS})

# MERCURY HL
add_library(mercury_hl ${MERCURY_HL_SRCS})
target_link_libraries(mercury_hl mercury)
mercury_set_lib_options(mercury_hl "mercury_hl" ${MERCURY_LIBTYPE})
if(MERCURY_ENABLE_COVERAGE)
  set_coverage_flags(mercury_hl)
endif()

set(MERCURY_EXPORTED_LIBS mercury_hl ${MERCURY_EXPORTED_LIBS})

#-----------------------------------------------------------------------------
# Specify project header files to be installed
#-----------------------------------------------------------------------------
set(MERCURY_HEADERS
  ${CMAKE_CURRENT_BINARY_DIR}/mercury_config.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_bulk.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_core.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_core_header.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_core_types.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_header.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_macros.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_proc_bulk.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_proc.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_types.h
  ${CMAKE_CURRENT_SOURCE_DIR}/proc_extra/mercury_proc_string.h
  ${CMAKE_CURRENT_SOURCE_DIR}/proc_extra/mercury_string_object.h
)
set(MERCURY_HL_HEADERS
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hl.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hl_macros.h
)

#-----------------------------------------------------------------------------
# Add file(s) to CMake Install
#-----------------------------------------------------------------------------
install(
  FILES
    ${MERCURY_HEADERS}
  DESTINATION
    ${MERCURY_INSTALL_INCLUDE_DIR}
  COMPONENT
    headers
)

install(
  FILES
    ${MERCURY_HL_HEADERS}
  DESTINATION
    ${MERCURY_INSTALL_INCLUDE_DIR}
  COMPONENT
    headers
)

if(MERCURY_USE_BOOST_PP AND NOT MERCURY_USE_SYSTEM_BOOST)
  install(
    DIRECTORY
      boost
    DESTINATION
      ${MERCURY_INSTALL_INCLUDE_DIR}
    COMPONENT
      headers
  )
endif()

#-----------------------------------------------------------------------------
# Add Target(s) to CMake Install
#-----------------------------------------------------------------------------
install(
  TARGETS
    mercury
  EXPORT
    ${MERCURY_EXPORTED_TARGETS}
  LIBRARY DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  ARCHIVE DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  RUNTIME DESTINATION ${MERCURY_INSTALL_BIN_DIR}
)

install(
  TARGETS
    mercury_hl
  EXPORT
    ${MERCURY_EXPORTED_TARGETS}
  LIBRARY DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  ARCHIVE DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  RUNTIME DESTINATION ${MERCURY_INSTALL_BIN_DIR}
)

#-----------------------------------------------------------------------------
# Add Target(s) to CMake Install for import into other projects
#-----------------------------------------------------------------------------
install(
  EXPORT
    ${MERCURY_EXPORTED_TARGETS}
  DESTINATION
    ${MERCURY_INSTALL_DATA_DIR}/cmake/${MERCURY_PACKAGE}
  FILE
    ${MERCURY_EXPORTED_TARGETS}.cmake
)

#-----------------------------------------------------------------------------
# Export all exported targets to the build tree for use by parent project
#-----------------------------------------------------------------------------
if(NOT MERCURY_EXTERNALLY_CONFIGURED)
  export(
    TARGETS
      ${MERCURY_EXPORTED_LIBS}
    FILE
      ${MERCURY_EXPORTED_TARGETS}.cmake
  )
endif()

#------------------------------------------------------------------------------
# Set variables for parent scope
#------------------------------------------------------------------------------
set(NA_PLUGINS ${NA_PLUGINS} PARENT_SCOPE)

# Pkg-config configuration
if(NOT WIN32)
  if(CMAKE_BUILD_TYPE)
    string(TOLOWER ${CMAKE_BUILD_TYPE} lower_cmake_build_type)
  endif()

  # Mercury package dependencies
  foreach(pkg_dep ${MERCURY_EXT_PKG_DEPENDENCIES})
    set(MERCURY_PKG_DEPENDENCIES "${MERCURY_PKG_DEPENDENCIES} ${pkg_dep}")
  endforeach()
  set(MERCURY_PKG_DEPENDENCIES ${MERCURY_PKG_DEPENDENCIES} PARENT_SCOPE)

  # Mercury private library dependencies
  foreach(exported_lib ${MERCURY_EXPORTED_LIBS})
    if(lower_cmake_build_type MATCHES "debug")
      get_target_property(MERCURY_LIBRARY ${exported_lib} OUTPUT_NAME_DEBUG)
    else()
      get_target_property(MERCURY_LIBRARY ${exported_lib} OUTPUT_NAME_RELEASE)
    endif()
    set(MERCURY_LIBRARIES "${MERCURY_LIBRARIES} -l${MERCURY_LIBRARY}")
  endforeach()
  set(MERCURY_LIBRARIES ${MERCURY_LIBRARIES} PARENT_SCOPE)

  # Mercury external library dependencies
  # Need to generate -llib if not already passed
  set(MERCURY_EXT_LIB_DEPENDENCIES
    ${MERCURY_EXT_LIB_DEPENDENCIES}
    ${MERCURY_EXT_PKG_LIB_DEPENDENCIES}
  )
  foreach(lib_dep ${MERCURY_EXT_LIB_DEPENDENCIES})
    # get library name
    get_filename_component(lib_name ${lib_dep} NAME_WE)
    if(lib_name MATCHES "^-l" OR lib_name MATCHES "-pthread")
      # lib_name found is -lxxx
      set(MERCURY_EXT_LIB_DEPENDENCIES_LIST
          ${MERCURY_EXT_LIB_DEPENDENCIES_LIST} ${lib_name})
    else()
      # lib_name is /path/to/lib so get library path and name
      get_filename_component(lib_path ${lib_dep} PATH)
      string(REGEX REPLACE "^lib" "" lib_name ${lib_name})
      set(MERCURY_EXT_LIB_DEPENDENCIES_LIST
          ${MERCURY_EXT_LIB_DEPENDENCIES_LIST} -L${lib_path} -l${lib_name})
    endif()
  endforeach()
  if(MERCURY_EXT_LIB_DEPENDENCIES_LIST)
    list(REMOVE_DUPLICATES MERCURY_EXT_LIB_DEPENDENCIES_LIST)
  endif()
  foreach(lib_dep ${MERCURY_EXT_LIB_DEPENDENCIES_LIST})
    set(MERCURY_LIB_DEPENDENCIES "${MERCURY_LIB_DEPENDENCIES} ${lib_dep}")
  endforeach()
  set(MERCURY_LIB_DEPENDENCIES ${MERCURY_LIB_DEPENDENCIES} PARENT_SCOPE)

  # External include dependencies
  set(MERCURY_EXT_INCLUDE_DEPENDENCIES
    ${MERCURY_EXT_INCLUDE_DEPENDENCIES}
    ${MERCURY_EXT_PKG_INCLUDE_DEPENDENCIES}
  )
  if(MERCURY_EXT_INCLUDE_DEPENDENCIES)
    list(REMOVE_DUPLICATES MERCURY_EXT_INCLUDE_DEPENDENCIES)
  endif()
  foreach(inc_dep ${MERCURY_EXT_INCLUDE_DEPENDENCIES})
    set(MERCURY_INCLUDE_DEPENDENCIES "${MERCURY_INCLUDE_DEPENDENCIES} -I${inc_dep}")
  endforeach()
  set(MERCURY_INCLUDE_DEPENDENCIES ${MERCURY_INCLUDE_DEPENDENCIES} PARENT_SCOPE)
endif()
